// Copyright (c) Appeon
// The following has been modified by Appeon under compliance with the Apache 2.0 license from the original work

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Linq;
using System.Linq.Expressions;
using JetBrains.Annotations;
using SnapObjects.Commons;

namespace SnapObjects.Data
{
    /// <summary>
    ///     Defines conversions from an object of one type in a model to an object of the same or
    ///     different type in the store.
    /// </summary>
    public abstract class ValueConverter
    {
        /// <summary>
        ///     Initializes a new instance of the <see cref="ValueConverter" /> class.
        /// </summary>
        internal ValueConverter()
        {
        }

        /// <summary>
        ///     Initializes a new instance of the <see cref="ValueConverter" /> class.
        /// </summary>
        /// <param name="convertToPropertyExpression">
        ///     The expression to convert objects when writing data to the store,
        ///     exactly as supplied and may not handle
        ///     nulls, boxing, and non-exact matches of simple types.
        /// </param>
        /// <param name="convertFromPropertyExpression">
        ///     The expression to convert objects when reading data from the store,
        ///     exactly as supplied and may not handle
        ///     nulls, boxing, and non-exact matches of simple types.
        /// </param>
        protected ValueConverter(
            [NotNull] LambdaExpression convertToPropertyExpression,
            [NotNull] LambdaExpression convertFromPropertyExpression)
        {
            Check.NotNull(convertToPropertyExpression, nameof(convertToPropertyExpression));
            Check.NotNull(convertFromPropertyExpression, nameof(convertFromPropertyExpression));

            this.ConvertToPropertyExpression = convertToPropertyExpression;
            this.ConvertFromPropertyExpression = convertFromPropertyExpression;
        }

        /// <summary>
        ///     Gets the function to convert objects when writing data to the store,
        ///     setup to handle nulls, boxing, and non-exact matches of simple types.
        /// </summary>
        public abstract Func<object, object> ConvertToProperty { get; }

        /// <summary>
        ///     Gets the function to convert objects when reading data from the store,
        ///     setup to handle nulls, boxing, and non-exact matches of simple types.
        /// </summary>
        public abstract Func<object, object> ConvertFromProperty { get; }

        /// <summary>
        ///     Gets the expression to convert objects when writing data to the store,
        ///     exactly as supplied and may not handle
        ///     nulls, boxing, and non-exact matches of simple types.
        /// </summary>
        public virtual LambdaExpression ConvertToPropertyExpression { get; }

        /// <summary>
        ///     Gets the expression to convert objects when reading data from the store,
        ///     exactly as supplied and may not handle
        ///     nulls, boxing, and non-exact matches of simple types.
        /// </summary>
        public virtual LambdaExpression ConvertFromPropertyExpression { get; }

        /// <summary>
        ///     The CLR type used in the model.
        /// </summary>
        public abstract Type PropertyClrType { get; }

        /// <summary>
        ///     The CLR type used when reading and writing from the store.
        /// </summary>
        public abstract Type DbFieldClrType { get; }

        public virtual bool CanConvert()
        {
            return true;
        }

        internal virtual bool CanConvert(Type fromType, Type toType)
        {
            return this.CanConvert();
        }
    }
}
